// Date:   Sun Aug 31 03:12:03 PM 2025
// Mail:   lunar_ubuntu@qq.com
// Author: https://github.com/xiaoqixian

#pragma once

#include <cstdint>
#include <string>
#include <unordered_map>
#include <utility>
#include <shared_mutex>
#include <vector>
extern "C" {
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavutil/imgutils.h>
#include <libavutil/avutil.h>
#include <libswscale/swscale.h>
#include <libavutil/frame.h>
}

/**
 * @brief A simple RAII wrapper for AVPacket.
 *
 * This class manages the lifetime of an FFmpeg AVPacket using RAII principles.
 * It ensures proper initialization, reference counting, move semantics,
 * and cleanup of packet resources.
 *
 * Features:
 * - Default constructor: initializes an empty packet.
 * - Construct from AVPacket*: creates a new reference to the given packet
 *   (using av_packet_ref), so the data remains valid independently of the
 *   original packet's lifetime.
 * - Deleted copy constructor: prevents accidental copying of packets, since
 *   AVPacket is non-trivial to copy safely.
 * - Move constructor: transfers ownership of the packet from another
 *   AVPacketWrapper, leaving the source in a valid but empty state.
 * - Destructor: automatically releases packet resources (av_packet_unref).
 *
 * This wrapper is useful for ensuring exception safety and avoiding memory
 * leaks or double-frees when working with AVPacket in C++.
 */
struct AVPacketWrapper {
  AVPacket pkt_;
  AVPacketWrapper() noexcept {
    av_init_packet(&pkt_);
  }
  AVPacketWrapper(const AVPacket* pkt) noexcept {
    av_init_packet(&pkt_);
    if (pkt) {
      av_packet_ref(&pkt_, pkt);
    }
  }
  AVPacketWrapper(AVPacketWrapper const& other) noexcept:AVPacketWrapper(&other.pkt_) {}
  AVPacketWrapper(AVPacketWrapper&& other) noexcept {
    pkt_ = other.pkt_;
    av_init_packet(&other.pkt_);
  }
  ~AVPacketWrapper() noexcept {
    av_packet_unref(&pkt_);
  }

  AVPacketWrapper& operator=(const AVPacketWrapper& other) noexcept {
    if (this != &other) {
      av_packet_unref(&pkt_);                // release current
      av_packet_ref(&pkt_, &other.pkt_);     // copy new
    }
    return *this;
  }

  AVPacketWrapper& operator=(AVPacketWrapper&& other) noexcept {
    if (this != &other) {
      av_packet_unref(&pkt_);                // release current
      pkt_ = other.pkt_;                     // steal ownership
      av_init_packet(&other.pkt_);           // reset other
    }
    return *this;
  }
};

/**
 * @brief A RAII wrapper for AVFrame, managing allocation, deallocation, and reference counting.
 *
 * This class encapsulates an FFmpeg AVFrame pointer, providing safe ownership semantics
 * and simplifying memory management. It supports deep copying and move semantics.
 *
 * Features:
 *  - Default constructor allocates a new AVFrame.
 *  - Construct from an existing AVFrame performs a deep copy (av_frame_ref).
 *  - Copy constructor and copy assignment perform deep copies of the underlying frame.
 *  - Move constructor and move assignment transfer ownership without copying.
 *  - Destructor frees the AVFrame properly (av_frame_free) to avoid memory leaks.
 *  - has_data() checks whether the frame contains valid image data.
 *
 * Usage:
 *  AVFrameWrapper frame;                     // allocate a new frame
 *  AVFrameWrapper copy(frame);               // deep copy
 *  AVFrameWrapper moved = std::move(frame); // transfer ownership
 */
struct AVFrameWrapper {
  AVFrame* frame_ = nullptr;

  // Default constructor
  AVFrameWrapper() noexcept {
    frame_ = av_frame_alloc();
  }

  // Construct from an existing frame (deep copy)
  AVFrameWrapper(const AVFrame* frame) noexcept {
    frame_ = av_frame_alloc();
    if (frame && frame_) {
      av_frame_ref(frame_, frame);
    }
  }

  // Copy constructor
  AVFrameWrapper(const AVFrameWrapper& other) noexcept {
    frame_ = av_frame_alloc();
    if (other.frame_ && frame_) {
      av_frame_ref(frame_, other.frame_);
    }
  }

  // Move constructor
  AVFrameWrapper(AVFrameWrapper&& other) noexcept
    : frame_(std::exchange(other.frame_, nullptr)) {}

  // Destructor
  ~AVFrameWrapper() noexcept {
    if (frame_) {
      av_frame_free(&frame_);
    }
  }

  // Copy assignment
  AVFrameWrapper& operator=(const AVFrameWrapper& other) noexcept {
    if (this != &other) {
      if (frame_) {
        av_frame_unref(frame_);
      } else {
        frame_ = av_frame_alloc();
      }
      if (other.frame_) {
        av_frame_ref(frame_, other.frame_);
      }
    }
    return *this;
  }

  // Move assignment
  AVFrameWrapper& operator=(AVFrameWrapper&& other) noexcept {
    if (this != &other) {
      if (frame_) {
        av_frame_free(&frame_);
      }
      frame_ = other.frame_;
      other.frame_ = nullptr;
    }
    return *this;
  }

  bool has_data() const {
    return frame_ != nullptr && frame_->data[0] != nullptr;
  }
};

/**
 * @brief RAII wrapper for AVCodecContext with source tracking.
 *
 * This class manages the lifetime of an AVCodecContext pointer and keeps
 * an auxiliary state (`last_src_id`) to record the last source ID used
 * for decoding.
 *
 * Key characteristics:
 * - Automatically frees the AVCodecContext when the object is destroyed.
 * - Movable but not copyable (prevents double-free issues).
 * - Provides constructors for raw pointer ownership and for initializing
 *   from codec + codec parameters.
 * - `last_src_id_` is preserved across moves and initialized to -1 by default.
 */
struct AVCodecCtxWrapper {
  AVCodecContext* codec_ctx_ = nullptr;  // Managed codec context

  // Default constructor
  AVCodecCtxWrapper() noexcept = default;

  // Construct from raw pointer (takes ownership)
  explicit AVCodecCtxWrapper(AVCodecContext* ctx) noexcept
      : codec_ctx_(ctx) {}

  // Construct from codec and codec parameters
  AVCodecCtxWrapper(AVCodec* codec, AVCodecParameters* codec_par) noexcept {
    codec_ctx_ = avcodec_alloc_context3(codec);
    avcodec_parameters_to_context(codec_ctx_, codec_par);
    codec_ctx_->flags |= AV_CODEC_FLAG_LOW_DELAY;
    avcodec_open2(codec_ctx_, codec, nullptr);
  }

  // Destructor
  ~AVCodecCtxWrapper() noexcept {
    if (codec_ctx_) {
      avcodec_free_context(&codec_ctx_);
    }
  }

  // Delete copy constructor and copy assignment
  AVCodecCtxWrapper(const AVCodecCtxWrapper&) = delete;
  AVCodecCtxWrapper& operator=(const AVCodecCtxWrapper&) = delete;

  // Move constructor
  AVCodecCtxWrapper(AVCodecCtxWrapper&& other) noexcept
      : codec_ctx_(std::exchange(other.codec_ctx_, nullptr))
        {}

  // Move assignment
  AVCodecCtxWrapper& operator=(AVCodecCtxWrapper&& other) noexcept {
    if (this != &other) {
      if (codec_ctx_) {
        avcodec_free_context(&codec_ctx_);
      }
      codec_ctx_ = std::exchange(other.codec_ctx_, nullptr);
    }
    return *this;
  }
};

/**
 * @brief RAII-style wrapper around FFmpeg decoding context.
 *
 * AVContext encapsulates the initialization, usage, and cleanup of
 * FFmpeg objects required to decode video from a file.
 *
 * Responsibilities:
 * - Open input file and allocate the AVFormatContext (demuxer).
 * - Find the video stream and initialize AVCodecContext for decoding.
 * - Store video parameters such as width, height, and pixel format.
 * - Optionally allocate a SwsContext for pixel format conversion or resizing.
 *
 * Features:
 * - Deleted copy constructor/assignment to avoid accidental shallow copies
 *   of FFmpeg pointers (which would cause double frees).
 * - Move constructor/assignment to allow safe transfer of ownership.
 * - Destructor automatically releases all FFmpeg resources (format context,
 *   codec context, sws context, etc.).
 *
 * Typical usage:
 * @code
 *   AVContext ctx("input.mp4");
 *   // use ctx.codec_ctx_, ctx.fmt_ctx_, etc. for decoding
 * @endcode
 */
struct AVContext {
  std::string filepath_;          // Input file path
  AVFormatContext* fmt_ctx_ = nullptr; // Format context (demux)
  AVCodecParameters* codec_par_ = nullptr;
  AVCodec* codec_ = nullptr;      // Decoder
  std::unordered_map<int, AVCodecCtxWrapper> codec_ctx_map_;
  std::shared_mutex codec_ctx_mtx_;
  int video_stream_index_ = -1;   // Index of the video stream

  AVPixelFormat pix_fmt_;         // Pixel format of decoded frames

  SwsContext* sws_ctx_ = nullptr; // For pixel format conversion / resize

  AVContext() noexcept = default;
  // Constructor / Destructor
  AVContext(std::string const& f);
  ~AVContext();

  AVContext(AVContext const&) = delete;
  AVContext& operator=(const AVContext&) = delete;
  AVContext(AVContext&&) noexcept;
  AVContext& operator=(AVContext&& other) noexcept;

  // seek to the start of the source
  void rewind();
};

struct VideoSegInfo {
  int label;
  int64_t start_pts = AV_NOPTS_VALUE;
  std::string url;
  VideoSegInfo() noexcept = default;
  VideoSegInfo(int64_t sp) noexcept: start_pts(sp) {}
};

std::vector<int64_t> compile_videos(std::vector<std::string> const& video_paths, const char* out_path);
